#include "windowthumbnail.h"

WindowThumbnail::WindowThumbnail(QWidget* parent)
    : mainLayout(new QVBoxLayout),
      combobox(new QComboBox),
      pictureBox(new QLabel),
      timer(new QTimer),
      screen(QGuiApplication::primaryScreen()),
      c(QX11Info::connection())
{
    display = XOpenDisplay(NULL);
    wins = getAllWindows();
    setFixedSize(640, 480);

    for (auto win : wins) {
        combobox->addItem("[WinId: " + QString::number((uint32_t)win) + "] " + getWindowName(win));
    }
    mainLayout->addWidget(combobox);
    mainLayout->addWidget(pictureBox,1);
    setLayout(mainLayout);

    timer->setInterval(100); // 10fps
    connect(timer, &QTimer::timeout, this, &WindowThumbnail::updateThumbnail);
    timer->start();
}

WindowThumbnail::~WindowThumbnail()
{

}

void WindowThumbnail::updateThumbnail()
{
    Window xid = wins[(combobox->currentIndex())];

    QImage img = grabWindowImage(xid).scaledToHeight(pictureBox->height());

    if(!img.isNull())
        pictureBox->setPixmap(QPixmap::fromImage(img));
    pictureBox->repaint();

}

QImage WindowThumbnail::grabWindowImage(Window w)
{
    xcb_window_t req_win_id = (xcb_window_t)w;

    xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(c, 0, 2);
    xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(c, comp_ver_cookie, NULL);
    if (!comp_ver_reply)
    {
        return QImage();
    }

    const xcb_setup_t *setup = xcb_get_setup(c);
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = screen_iter.data;
    // request redirection of window
    xcb_void_cookie_t cookie = xcb_composite_redirect_window_checked(c, req_win_id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
    int win_h, win_w, win_d;

    xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(c, req_win_id);
    xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(c, gg_cookie, NULL);
    if (gg_reply) {
        win_w = gg_reply->width;
        win_h = gg_reply->height;
        win_d = gg_reply->depth;
        free(gg_reply);
    } else {
        return QImage();
    }

    // create a pixmap
    xcb_pixmap_t win_pixmap = xcb_generate_id(c);
    xcb_composite_name_window_pixmap(c, req_win_id, win_pixmap);

    // get the image
    xcb_get_image_cookie_t gi_cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
    QSharedPointer<xcb_get_image_reply_t> gi_reply(xcb_get_image_reply(c, gi_cookie, NULL));
    if (gi_reply) {
        int data_len = xcb_get_image_data_length(gi_reply.data());
        uint8_t *data = xcb_get_image_data(gi_reply.data());
        if(win_d == 32) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_ARGB32_Premultiplied);
            return img.copy();
        } else if(win_d == 24) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_RGB32);
            return img.copy();
        }
    }
    return QImage();
}

bool WindowThumbnail::ewmhIsSupported()
{
    Atom netSupportingWmCheck =
        XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
    Atom actualType;
    int format = 0;
    unsigned long num = 0, bytes = 0;
    unsigned char *data = NULL;
    Window ewmh_window = 0;

    int status = XGetWindowProperty(display, DefaultRootWindow(display),
                    netSupportingWmCheck, 0L, 1L, false,
                    XA_WINDOW, &actualType, &format, &num,
                    &bytes, &data);

    if (status == Success) {
        if (num > 0) {
            ewmh_window = ((Window *)data)[0];
        }
        if (data) {
            XFree(data);
            data = NULL;
        }
    }

    if (ewmh_window) {
        status = XGetWindowProperty(display, ewmh_window,
                        netSupportingWmCheck, 0L, 1L, false,
                        XA_WINDOW, &actualType, &format,
                        &num, &bytes, &data);
        if (status != Success || num == 0 ||
            ewmh_window != ((Window *)data)[0]) {
            ewmh_window = 0;
        }
        if (status == Success && data) {
            XFree(data);
        }
    }

    return ewmh_window != 0;
}

QString WindowThumbnail::getWindowAtom(Window win, const char *atom)
{
    Atom netWmName = XInternAtom(display, atom, false);
    int n;
    char **list = 0;
    XTextProperty tp;
    QString res = "unknown";

    XGetTextProperty(display, win, &tp, netWmName);

    if (!tp.nitems)
        XGetWMName(display, win, &tp);

    if (!tp.nitems)
        return "error";

    if (tp.encoding == XA_STRING) {
        res = (char *)tp.value;
    } else {
        int ret = XmbTextPropertyToTextList(display, &tp, &list, &n);

        if (ret >= Success && n > 0 && *list) {
            res = *list;
            XFreeStringList(list);
        }
    }

    XFree(tp.value);

    QString str = res.toUtf8();
    return res;
}

QList<Window> WindowThumbnail::getAllWindows()
{
    QList<Window> res;

    if (!ewmhIsSupported()) {
        qDebug()<< "Unable to query window list because window manager does not support extended window manager Hints";
        return res;
    }

    Atom netClList = XInternAtom(display, "_NET_CLIENT_LIST", true);
    Atom actualType;
    int format;
    unsigned long num, bytes;
    Window *data = 0;

    for (int i = 0; i < ScreenCount(display); ++i) {
        Window rootWin = RootWindow(display, i);

        int status = XGetWindowProperty(display, rootWin, netClList, 0L,
                        ~0L, false, AnyPropertyType,
                        &actualType, &format, &num,
                        &bytes, (uint8_t **)&data);

        if (status != Success) {
            continue;
        }

        for (unsigned long i = 0; i < num; ++i)
            res.push_back(data[i]);
            qDebug()<<res;
        XFree(data);
    }

    return res;

}

